<template>
<view class="container">
<!-- 顶部导航栏 -->

<!-- 设备搜索区域 -->
<view class="search-section">
<view class="searching" v-if="isScanning">
<uni-icons type="spinner-cycle" size="24" color="#999" />
<text class="searching-text">正在搜索设备...</text>
</view>
<scroll-view class="device-list" scroll-y>
<view
v-for="(device, index) in deviceList"
:key="index"
class="device-card"
@click="connectDevice(device)"
>
<view class="device-info">
<text class="device-name">{{device.name || '未知设备'}}</text>
<view class="device-meta">
<uni-icons
type="list"
size="16"
:color="getSignalColor(device.RSSI)"
/>
<text class="device-rssi">{{device.RSSI}}dBm</text>
<text class="device-mac">{{device.deviceId}}</text>
</view>
</view>
<uni-icons
type="checkmarkempty"
size="24"
color="#07C160"
v-if="currentDevice && currentDevice.deviceId === device.deviceId"
/>

</view>
</scroll-view>
</view>
<!-- 设备操作区域 -->
<view class="operation-section" v-if="currentDevice">
<text class="section-title">设备配网</text>
<view class="device-detail">
<text class="detail-title">当前设备:</text>
<text class="detail-value">{{currentDevice.name || '未知设备'}}</text>
</view>
<view class="form-item">
<text class="form-label">WiFi名称</text>
<input
v-model="wifiName"
class="form-input"
placeholder="请输入WiFi名称"
placeholder-class="placeholder"
/>
</view>
<view class="form-item">
<text class="form-label">WiFi密码</text>
<input
v-model="wifiPassword"
class="form-input"
placeholder="请输入WiFi密码"
password
placeholder-class="placeholder"
/>
</view>
<button
class="confirm-btn"
:disabled="isConfiguring"
@click="startConfig"
>
{{isConfiguring ? '配网中...' : '确认配网'}}
</button>
<view class="status-tip" v-if="configStatus">
<text :class="['status-text', configStatus.type]">{{configStatus.message}}</text>
</view>
<button
class="disconnect-btn"
@click="disconnectDevice"
>
断开连接
</button>
</view>
<!-- 底部功能区 -->
<view class="footer">
<button class="footer-btn" @click="scanDevices">
<uni-icons type="refresh" size="20" color="#333" />
<text>刷新搜索</text>
</button>
<button class="footer-btn" @click="showHelp">
<uni-icons type="help" size="20" color="#333" />
<text>帮助说明</text>
</button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { onShow, onHide } from '@dcloudio/uni-app';
interface DeviceInfo {
name: string;
deviceId: string;
RSSI: number;
connected?: boolean;
}
const isScanning = ref(false);
const deviceList = ref<DeviceInfo[]>([]);
let currentDevice = ref<DeviceInfo | null>(null);
const wifiName = ref('');
const wifiPassword = ref('');
const isConfiguring = ref(false);
const configStatus = reactive({
type: '',
message: ''
});
const bluetoothStatus = reactive({
icon: 'list',
color: '#999'
});
const getSignalColor = (rssi: number) => {
if (rssi > -50) return '#07C160';
if (rssi > -70) return '#FF9500';
return '#FF3B30';
};
const scanDevices = () => {
isScanning.value = true;
deviceList.value = [];
currentDevice = ref<DeviceInfo | null>(null);
// 模拟搜索设备
setTimeout(() => {
deviceList.value = [
{ name: 'ESP32-001', deviceId: 'A4:C1:38:12:34:56', RSSI: -45 },
{ name: 'ESP32-002', deviceId: 'B5:D2:47:23:45:67', RSSI: -65 },
{ name: 'ESP32-003', deviceId: 'C6:E3:56:34:56:78', RSSI: -75 }
];
isScanning.value = false;
}, 2000);
};
const connectDevice = (device: DeviceInfo) => {
currentDevice.value = device;
bluetoothStatus.icon = 'list';
bluetoothStatus.color = '#007AFF';
uni.showToast({
title: '设备已连接',
icon: 'success'
});
};
const disconnectDevice = () => {
currentDevice.value = null;
bluetoothStatus.icon = 'list';
bluetoothStatus.color = '#999';
uni.showToast({
title: '设备已断开',
icon: 'none'
});
};
const startConfig = () => {
if (!wifiName.value) {
uni.showToast({
title: '请输入WiFi名称',
icon: 'none'
});
return;
}
isConfiguring.value = true;
configStatus.type = 'info';
configStatus.message = '正在配网，请稍候...';
// 模拟配网过程
setTimeout(() => {
isConfiguring.value = false;
const success = Math.random() > 0.3;
if (success) {
configStatus.type = 'success';
configStatus.message = '配网成功！设备正在连接WiFi...';
uni.showToast({
title: '配网成功',
icon: 'success'
});
} else {
configStatus.type = 'error';
configStatus.message = '配网失败，请重试';
uni.showToast({
title: '配网失败',
icon: 'none'
});
}
}, 3000);
};
const showHelp = () => {
uni.showModal({
title: '帮助说明',
content: '1. 点击"刷新搜索"按钮扫描附近设备\n2. 点击设备卡片连接设备\n3. 输入WiFi名称和密码\n4. 点击"确认配网"按钮开始配网',
showCancel: false
});
};
onShow(() => {
scanDevices();
});
onHide(() => {
if (currentDevice.value) {
disconnectDevice();
}
});
</script>
<style>
page {
height: 100%;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
background: linear-gradient(180deg, #f8f9ff 0%, #f0f4ff 100%);
padding: 32rpx;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
margin-bottom: 40rpx;
}
.title {
font-size: 40rpx;
font-weight: 600;
color: #1a1a1a;
letter-spacing: 0.5px;
}
.search-section {
flex: 1;
margin-bottom: 30rpx;
}
.section-title {
display: block;
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.searching {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx 0;
}
.searching-text {
font-size: 28rpx;
color: #999;
margin-left: 10rpx;
}
.device-list {
flex: 1;
overflow: auto;
background-color: #fff;
border-radius: 24rpx;
padding: 24rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04);
border: 1px solid rgba(0, 0, 0, 0.04);
}
.device-card {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
margin-bottom: 24rpx;
background-color: #fff;
border-radius: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.03);
transition: all 0.2s ease;
}
.device-card:active {
background-color: #eee;
}
.device-info {
flex: 1;
}
.device-name {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 10rpx;
}
.device-meta {
display: flex;
align-items: center;
}
.device-rssi {
font-size: 24rpx;
color: #666;
margin: 0 10rpx 0 5rpx;
}
.device-mac {
font-size: 24rpx;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 300rpx;
}
.operation-section {
background-color: #fff;
border-radius: 24rpx;
padding: 40rpx;
margin-bottom: 40rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04);
border: 1px solid rgba(0, 0, 0, 0.04);
}
.device-detail {
margin-bottom: 30rpx;
}
.detail-title {
font-size: 28rpx;
color: #666;
margin-right: 10rpx;
}
.detail-value {
font-size: 28rpx;
color: #333;
}
.form-item {
margin-bottom: 30rpx;
}
.form-label {
display: block;
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.form-input {
height: 80rpx;
width: 100%;
border: 1px solid #ddd;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.placeholder {
color: #ccc;
}
.confirm-btn {
background: linear-gradient(90deg, #3a7bfd 0%, #4facfe 100%);
color: #fff;
height: 96rpx;
line-height: 96rpx;
border-radius: 48rpx;
font-size: 34rpx;
font-weight: 500;
margin: 40rpx 0 24rpx;
box-shadow: 0 8rpx 24rpx rgba(58, 123, 253, 0.2);
}
.confirm-btn[disabled] {
background-color: #7FBBFF;
}
.disconnect-btn {
background-color: #fff;
color: #FF3B30;
height: 84rpx;
line-height: 84rpx;
border: 1px solid #FF3B30;
border-radius: 42rpx;
font-size: 32rpx;
font-weight: 500;
margin-top: 24rpx;
transition: all 0.2s ease;
}
.status-tip {
text-align: center;
margin: 20rpx 0;
}
.status-text {
font-size: 26rpx;
}
.status-text.info {
color: #007AFF;
}
.status-text.success {
color: #07C160;
}
.status-text.error {
color: #FF3B30;
}
.footer {
display: flex;
justify-content: space-between;
padding: 24rpx 0;
background-color: #fff;
border-top: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.02);
}
.footer-btn {
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
color: #666;
font-size: 30rpx;
font-weight: 500;
height: 84rpx;
line-height: 84rpx;
flex: 1;
margin: 0 12rpx;
border-radius: 42rpx;
transition: all 0.2s ease;
}
.footer-btn:active {
background-color: rgba(0, 0, 0, 0.03);
}
.footer-btn text {
margin-left: 10rpx;
}
</style>
